package gogen

import (
	"fmt"
	"github.com/tal-tech/go-zero/tools/goctl/api/spec"
	"github.com/tal-tech/go-zero/tools/goctl/config"
	"github.com/tal-tech/go-zero/tools/goctl/internal/version"
	"github.com/tal-tech/go-zero/tools/goctl/util"
	"github.com/tal-tech/go-zero/tools/goctl/util/format"
	"github.com/tal-tech/go-zero/tools/goctl/vars"
	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
	"path"
	"strings"
)

const (
	handlerImports       = `package handler

import (
	"github.com/gin-gonic/gin"
	{{.packages}}
)
`
	defaultLogicPackage = "logic"
	handlerTemplate     = `package {{.PkgName}}

import (
	"github.com/gin-gonic/gin"
	{{if .After1_1_10}}"github.com/zeromicro/go-zero/rest/httpx"{{end}}
	{{.ImportPackages}}
)

func {{.HandlerName}}(c *gin.Context){
	{{if .HasRequest}}var req types.{{.RequestType}}
		if err := c.ShouldBind(&req); err != nil {
			c.JSON(200,gin.H{"code":400,"msg":err.Error(),"data":nil})
			return
		}{{end}}
		l := {{.LogicName}}.New{{.LogicType}}(c,svc.Svc)
		{{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}req{{end}})
		if err != nil {
			c.JSON(200,gin.H{"code":400,"msg":err.Error(),"data":nil})
		} else {
			{{if .HasResp}}c.JSON(200, resp){{else}}c.JSON(200, nil){{end}}
		}
}
`
)

//type Handler struct {
//	HandlerName string
//	RequestType string
//	LogicType   string
//	LogicName   string
//	Call        string
//	HasResp     bool
//	HasRequest  bool
//}

//func genHandlerImports(group spec.Group, route spec.Route, parentPkg string) []string {
//	var imports []string
//	imports = append(imports, fmt.Sprintf("\"%s\"",
//		joinPackages(parentPkg, getLogicFolderPath(group, route))))
//	imports = append(imports, fmt.Sprintf("\"%s\"", joinPackages(parentPkg, contextDir)))
//	if len(route.RequestTypeName()) > 0 {
//		imports = append(imports, fmt.Sprintf("\"%s\"\n", joinPackages(parentPkg, typesDir)))
//	}
//	return imports
//}

type handlerInfo struct {
	PkgName        string
	ImportPackages string
	HandlerName    string
	RequestType    string
	LogicName      string
	LogicType      string
	Call           string
	HasResp        bool
	HasRequest     bool
	After1_1_10    bool
}

func genHandler(dir, rootPkg string, cfg *config.Config, group spec.Group, route spec.Route) error {
	handler := getHandlerBaseName(route)
	handlerPath := getHandlerFolderPath(group, route)
	pkgName := handlerPath[strings.LastIndex(handlerPath, "/")+1:]
	logicName := defaultLogicPackage
	if handlerPath != handlerDir {
		handler = strings.Title(handler)
		logicName = pkgName
	}
	parentPkg, err := getParentPackage(dir)
	if err != nil {
		return err
	}

	goctlVersion := version.GetGoctlVersion()
	// todo(anqiansong): This will be removed after a certain number of production versions of goctl (probably 5)
	after1_1_10 := version.IsVersionGreaterThan(goctlVersion, "1.1.10")
	return doGenToFile(dir, handler, cfg, group, route, handlerInfo{
		PkgName:        pkgName,
		ImportPackages: genHandlerImports(group, route, parentPkg),
		HandlerName:    handler,
		After1_1_10:    after1_1_10,
		RequestType:    util.Title(route.RequestTypeName()),
		LogicName:      logicName,
		//LogicType:      strings.Title(getLogicName(route)), //
		LogicType:      strings.Title(getHandlerBaseName(route)),
		Call:           strings.Title(strings.TrimSuffix(handler, "Handler")),
		HasResp:        len(route.ResponseTypeName()) > 0,
		HasRequest:     len(route.RequestTypeName()) > 0,
	})
}

func doGenToFile(dir, handler string, cfg *config.Config, group spec.Group,
	route spec.Route, handleObj handlerInfo) error {
	filename, err := format.FileNamingFormat(cfg.NamingFormat, handler)
	if err != nil {
		return err
	}

	return genFile(fileGenConfig{
		dir:             dir,
		subdir:          getHandlerFolderPath(group, route),
		filename:        filename + ".go",
		templateName:    "handlerTemplate",
		category:        category,
		templateFile:    handlerTemplateFile,
		builtinTemplate: handlerTemplate,
		data:            handleObj,
	})
}

func genHandlers(dir, rootPkg string, cfg *config.Config, api *spec.ApiSpec) error {
	for _, group := range api.Service.Groups {
		for _, route := range group.Routes {
			if err := genHandler(dir, rootPkg, cfg, group, route); err != nil {
				return err
			}
		}
	}
	return nil
}

func genHandlerImports(group spec.Group, route spec.Route, parentPkg string) string {
	var imports []string
	imports = append(imports, fmt.Sprintf("\"%s\"",
		pathx.JoinPackages(parentPkg, getLogicFolderPath(group, route))))
	imports = append(imports, fmt.Sprintf("\"%s\"", pathx.JoinPackages(parentPkg, contextDir)))
	if len(route.RequestTypeName()) > 0 {
		imports = append(imports, fmt.Sprintf("\"%s\"\n", pathx.JoinPackages(parentPkg, typesDir)))
	}

	currentVersion := version.GetGoctlVersion()
	// todo(anqiansong): This will be removed after a certain number of production versions of goctl (probably 5)
	if !version.IsVersionGreaterThan(currentVersion, "1.1.10") {
		imports = append(imports, fmt.Sprintf("\"%s/rest/httpx\"", vars.ProjectOpenSourceURL))
	}

	return strings.Join(imports, "\n\t")
}

func joinPackages(pkgs ...string) string {
	return strings.Join(pkgs, "/")
}

func getHandlerBaseName(route spec.Route) string {
	handler := route.Handler
	handler = strings.TrimSpace(handler)
	handler = strings.TrimSuffix(handler, "handler")
	handler = strings.TrimSuffix(handler, "Handler")
	return handler
}

func getHandlerFolderPath(group spec.Group, route spec.Route) string {
	folder := route.GetAnnotation(groupProperty)// 这个 folder 就是 @server(group) 中 group 这个 key 对应的值
	if len(folder) == 0 {
		folder = group.GetAnnotation(groupProperty)
		if len(folder) == 0 {
			return handlerDir // 输出 internal/handler
		}
	}
	folder = strings.TrimPrefix(folder, "/")
	folder = strings.TrimSuffix(folder, "/")
	return path.Join(handlerDir, folder)
}

//func getHandlerName(route spec.Route, folder string) string {
//	handler  := getHandlerBaseName(route)
//	if folder != handlerDir {
//		handler = strings.Title(handler)
//	}
//	return handler
//}

//func genHandlers(dir,rootPkg string, cfg *config.Config, api *spec.ApiSpec) error {
//	if len(cfg.NamingFormat)==0 {
//		cfg.NamingFormat = "go_zero"
//	}
//
//	for _, g := range api.Service.Groups {
//		if len(g.Routes) == 0 {
//			continue
//		}
//		folder := getHandlerFolderPath(g, g.Routes[0]) // folder 默认为: internal/handler +"/"+ group.Route 或者 internal/handler
//		/*
//		当 @server(
//		) 中没有定义 group时 folder == internal/handler
//		 */
//		for _, r := range g.Routes {
//			filename,err := format.FileNamingFormat(cfg.NamingFormat, getHandlerName(r, folder))// getHandlerName()的返回值就是 .api 文件中 handler 的
//			if err != nil {
//				panic(err)
//			}
//			filename = filename + ".go"
//			os.Remove(filepath.Join(dir, getHandlerFolderPath(g, r), filename))
//		}
//		err := gen(folder,rootPkg, g, dir, cfg)
//		if err != nil {
//			debug.PrintStack()
//			return err
//		}
//	}
//	return nil
//}

//func gen(folder ,rootPkg string, group spec.Group, dir string, cfg *config.Config) error {
//	filename, err := format.FileNamingFormat(cfg.NamingFormat, "handler")
//	if err != nil {
//		return err
//	}
//	filename = filename + ".go" // filename 默认为:handlers.go
//	filename = filepath.Join(dir, folder, filename) // /Users/lo/go/src/Practice/go-crud/demo/internal/handler/base/handlers.go (dir 默认为当前项目的绝对路径 + c.String("dir"),folder 默认为: internal/handler +"/"+ group.Route
//	dir2 := filepath.Join(dir,folder)
//	var (
//		fp *os.File
//		hasExist = true
//	)
//	if !pathExist(dir2) {
//		err := os.MkdirAll(dir2,os.ModePerm)
//		if err != nil {
//			return err
//		}
//		hasExist = false
//	}
//
//	fp, err = os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0600)
//	if err != nil {
//		return err
//	}
//	defer fp.Close()
//
//	text, err := util.LoadTemplate("api", "handlers.tpl", handlerTemplate)
//	if err != nil {
//		return err
//	}
//	var (
//		funcs []string
//		imports []string
//	)
//	for _, route := range group.Routes {
//		handler := getHandlerName(route, folder)
//		handlerPath := getHandlerFolderPath(group, route)
//		pkgName := handlerPath[strings.LastIndex(handlerPath, "/")+1:]
//		logicName := "logic"
//		if handlerPath != handlerDir {
//			handler = strings.Title(handler)
//			logicName = pkgName
//		}
//		if hasExist && funcExist(filename, handler) {
//			continue
//		}
//		handleObj := Handler{
//			HandlerName: handler,
//			RequestType: strings.Title(route.RequestTypeName()),
//			LogicType:   strings.Title(logicName),
//			LogicName:   logicName,
//			Call:        strings.Title(strings.TrimSuffix(handler, "Handler")),
//			HasResp:     len(route.ResponseTypeName()) > 0,
//			HasRequest:  len(route.RequestTypeName()) > 0,
//		}
//
//		buffer := new(bytes.Buffer)
//		err = template.Must(template.New("handlerTemplate").Parse(text)).Execute(buffer, handleObj)
//		if err != nil {
//			return err
//		}
//
//		funcs = append(funcs, buffer.String())
//
//		for _, item := range genHandlerImports(group, route, rootPkg) {
//			if !stringx.Contains(imports, item) {
//				imports = append(imports, item)
//			}
//		}
//	}
//
//	buffer := new(bytes.Buffer)
//	if !hasExist {
//		importsStr := strings.Join(imports, "\n\t")
//		err = template.Must(template.New("handlerImports").Parse(handlerImports)).Execute(buffer, map[string]string{
//			"packages": importsStr,
//		})
//		if err != nil {
//			return err
//		}
//	}
//	formatCode := formatCode(strings.ReplaceAll(buffer.String(), "&#34;", "\"") + strings.Join(funcs, "\n\n"))
//	content, err := ioutil.ReadFile(filename)
//	if err != nil {
//		return err
//	}
//
//	if len(content) > 0 {
//		formatCode = string(content) + "\n" + formatCode
//	}
//	_, err = fp.WriteString(formatCode)
//	return err
//}

//func funcExist(filename, funcName string) bool {
//	data, err := ioutil.ReadFile(filename)
//	if err != nil {
//		return false
//	}
//	set := token.NewFileSet()
//	packs, err := parser.ParseFile(set, filename, string(data), parser.ParseComments)
//	if err != nil {
//		panic(err)
//	}
//	for _, d := range packs.Decls {
//		if fn, isFn := d.(*ast.FuncDecl); isFn {
//			if fn.Name.String() == funcName {
//				return true
//			}
//		}
//	}
//	return false
//}
//
//// 返回 true 说明文件或文件夹存在
//func pathExist(path string) bool {
//	_, err := os.Stat(path)    //os.Stat获取文件信息
//	if os.IsNotExist(err) {
//		return false
//	}
//	return true
//}

//goctl api plugin -p goctl-go-compact -api go-crud.api -dir .
